﻿#pragma once

#include <QMutex>
#include <QMap>

#include <include/cef_app.h>
#include <include/cef_browser.h>
#include <include/cef_client.h>
#include <include/cef_version.h>
#include <include/wrapper/cef_resource_manager.h>
#include <include/wrapper/cef_message_router.h>

#include "CefWebEngineCore/CefWebEngine.h"

class CefWebEnginePage;
class CefWebEnginePrivate;
class CefViewQueryHandler;

class InternalCefApp : public CefApp, public CefBrowserProcessHandler
{
public:
    InternalCefApp(CefWebEnginePrivate *webEngine, const QString &bridgeName = "CefWebEngine");
    ~InternalCefApp();

    static void RegisterCustomSchemesHandlerFactories();
    static void RegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar);

    void OnBeforeCommandLineProcessing(const CefString &process_type,
        CefRefPtr<CefCommandLine> command_line) override;
    void OnRegisterCustomSchemes(CefRawPtr<CefSchemeRegistrar> registrar) override;
    
public: 
    CefRefPtr<CefBrowserProcessHandler> GetBrowserProcessHandler() override;
    void OnContextInitialized() override;
    void OnBeforeChildProcessLaunch(CefRefPtr<CefCommandLine> command_line) override;
    void OnScheduleMessagePumpWork(int64_t delay_ms) override;

private:
    QString mbridgeName;
    CefWebEnginePrivate *mwebEngine;
    IMPLEMENT_REFCOUNTING(InternalCefApp);
};

class InternalCefClient : public CefClient,
    public CefLifeSpanHandler,
    public CefContextMenuHandler,
    public CefDialogHandler,
    public CefDisplayHandler,
    public CefKeyboardHandler,
    public CefLoadHandler,
    public CefRenderHandler,
    public CefDragHandler,
    public CefFocusHandler,
    public CefJSDialogHandler,
    public CefRequestHandler,
    public CefDownloadHandler,
    public CefResourceRequestHandler
{
public:
    InternalCefClient(CefWebEnginePrivate *webEngine);
    ~InternalCefClient();
    
public:
    CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
    bool OnBeforePopup(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
#if CEF_VERSION_MAJOR >= 139
        int popup_id,
#endif
        const CefString &target_url,
        const CefString &target_frame_name,
        CefLifeSpanHandler::WindowOpenDisposition target_disposition,
        bool user_gesture,
        const CefPopupFeatures &popupFeatures,
        CefWindowInfo &windowInfo,
        CefRefPtr<CefClient> &client,
        CefBrowserSettings &settings,
        CefRefPtr<CefDictionaryValue> &extra_info,
        bool *no_javascript_access) override;

    void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
    bool DoClose(CefRefPtr<CefBrowser> browser) override;
    void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;

public:
    CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override;
    void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefContextMenuParams> params,
        CefRefPtr<CefMenuModel> model) override;
    bool RunContextMenu(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefContextMenuParams> params,
        CefRefPtr<CefMenuModel> model,
        CefRefPtr<CefRunContextMenuCallback> callback) override;
    bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefContextMenuParams> params,
        int command_id,
        EventFlags event_flags) override;
    void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame) override;

public:
    CefRefPtr<CefDialogHandler> GetDialogHandler() override;
    bool OnFileDialog(CefRefPtr<CefBrowser> browser,
        FileDialogMode mode,
        const CefString &title,
        const CefString &default_file_path,
        const std::vector<CefString> &accept_filters,
#if CEF_VERSION_MAJOR >= 139
        const std::vector<CefString> &accept_extensions,
        const std::vector<CefString> &accept_descriptions,
#endif
        CefRefPtr<CefFileDialogCallback> callback) override;

public:
    CefRefPtr<CefDisplayHandler> GetDisplayHandler() override;
    void OnAddressChange(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, const CefString &url) override;
    void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString &title) override;
    void OnFaviconURLChange(CefRefPtr<CefBrowser> browser, const std::vector<CefString> &icon_urls) override;
    void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser, bool fullscreen) override;
    bool OnTooltip(CefRefPtr<CefBrowser> browser, CefString &text) override;
    void OnStatusMessage(CefRefPtr<CefBrowser> browser, const CefString &value) override;
    bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
        cef_log_severity_t level,
        const CefString &message,
        const CefString &source,
        int line) override;
    bool OnAutoResize(CefRefPtr<CefBrowser> browser, const CefSize &new_size) override;
    bool OnCursorChange(CefRefPtr<CefBrowser> browser,
        CefCursorHandle cursor,
        cef_cursor_type_t type,
        const CefCursorInfo &custom_cursor_info) override;

public:
    CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() override;
    bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
        const CefKeyEvent &event,
        CefEventHandle os_event,
        bool *is_keyboard_shortcut) override;
    bool OnKeyEvent(CefRefPtr<CefBrowser> browser, const CefKeyEvent &event, CefEventHandle os_event) override;

public:
    CefRefPtr<CefLoadHandler> GetLoadHandler() override;
    void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
        bool isLoading,
        bool canGoBack,
        bool canGoForward) override;
    void OnLoadStart(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        TransitionType transition_type) override;
    void OnLoadEnd(CefRefPtr<CefBrowser> browser, CefRefPtr<CefFrame> frame, int httpStatusCode) override;
    void OnLoadError(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        ErrorCode errorCode,
        const CefString &errorText,
        const CefString &failedUrl) override;
    void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser, double progress) override;

public:
    CefRefPtr<CefRenderHandler> GetRenderHandler() override;
    CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() override;
    bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override;
    void GetViewRect(CefRefPtr<CefBrowser> browser, CefRect &rect) override;
    bool GetScreenPoint(CefRefPtr<CefBrowser> browser, int viewX, int viewY, int &screenX, int &screenY) override;
    bool GetScreenInfo(CefRefPtr<CefBrowser> browser, CefScreenInfo &screen_info) override;
    void OnPopupShow(CefRefPtr<CefBrowser> browser, bool show) override;
    void OnPopupSize(CefRefPtr<CefBrowser> browser, const CefRect &rect) override;
    void OnPaint(CefRefPtr<CefBrowser> browser,
        PaintElementType type,
        const RectList &dirtyRects,
        const void *buffer,
        int width,
        int height) override;
    void OnAcceleratedPaint(CefRefPtr<CefBrowser> browser,
        PaintElementType type,
        const RectList &dirtyRects,
#if CEF_VERSION_MAJOR >= 139
        const CefAcceleratedPaintInfo &info
#else
        void *shared_handle
#endif
    ) override;

    bool StartDragging(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefDragData> drag_data,
        CefRenderHandler::DragOperationsMask allowed_ops,
        int x,
        int y) override;
    void UpdateDragCursor(CefRefPtr<CefBrowser> browser, DragOperation operation) override;
    void OnScrollOffsetChanged(CefRefPtr<CefBrowser> browser, double x, double y) override;
    void OnImeCompositionRangeChanged(CefRefPtr<CefBrowser> browser,
        const CefRange &selected_range,
        const RectList &character_bounds) override;
    void OnTextSelectionChanged(CefRefPtr<CefBrowser> browser,
        const CefString &selected_text,
        const CefRange &selected_range) override;
    void OnVirtualKeyboardRequested(CefRefPtr<CefBrowser> browser, TextInputMode input_mode) override;

public:
    CefRefPtr<CefDragHandler> GetDragHandler() override;
    bool OnDragEnter(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefDragData> dragData,
        CefDragHandler::DragOperationsMask mask) override;

    void OnDraggableRegionsChanged(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        const std::vector<CefDraggableRegion> &regions) override;

public:
    CefRefPtr<CefFocusHandler> GetFocusHandler() override;
    void OnTakeFocus(CefRefPtr<CefBrowser> browser, bool next) override;
    bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) override;
    void OnGotFocus(CefRefPtr<CefBrowser> browser) override;

public:
    virtual CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override;
    virtual bool OnJSDialog(CefRefPtr<CefBrowser> browser,
        const CefString &origin_url,
        JSDialogType dialog_type,
        const CefString &message_text,
        const CefString &default_prompt_text,
        CefRefPtr<CefJSDialogCallback> callback,
        bool &suppress_message) override;

    virtual bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
        const CefString &message_text,
        bool is_reload,
        CefRefPtr<CefJSDialogCallback> callback) override;
    virtual void OnResetDialogState(CefRefPtr<CefBrowser> browser) override;

public:
    CefRefPtr<CefRequestHandler> GetRequestHandler() override;
    bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request,
        bool user_gesture,
        bool is_redirect) override;

    bool OnOpenURLFromTab(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        const CefString &target_url,
        CefRequestHandler::WindowOpenDisposition target_disposition,
        bool user_gesture) override;
#if CEF_VERSION_MAJOR > 91 && CEF_VERSION_MAJOR < 110
    bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
        const CefString &origin_url,
        int64 new_size,
        CefRefPtr<CefCallback> callback) override;
#endif

    void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
        TerminationStatus status
#if CEF_VERSION_MAJOR >= 139
        , int error_code,
        const CefString &error_string
#endif
    ) override;

public:
    CefRefPtr<CefDownloadHandler> GetDownloadHandler() override;

#if CEF_VERSION_MAJOR >= 139
    bool
#else
    void
#endif
    OnBeforeDownload(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefDownloadItem> download_item,
        const CefString &suggested_name,
        CefRefPtr<CefBeforeDownloadCallback> callback) override;

    void OnDownloadUpdated(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefDownloadItem> download_item,
        CefRefPtr<CefDownloadItemCallback> callback) override;

public:
    CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request,
        bool is_navigation,
        bool is_download,
        const CefString &request_initiator,
        bool &disable_default_handling) override;

    ReturnValue OnBeforeResourceLoad(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request,
        CefRefPtr<CefCallback> callback) override;

    CefRefPtr<CefResourceHandler> GetResourceHandler(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request) override;

    void OnProtocolExecution(CefRefPtr<CefBrowser> browser,
        CefRefPtr<CefFrame> frame,
        CefRefPtr<CefRequest> request,
        bool &allow_os_execution) override;

private:
    CefWebEnginePage *findPage(CefRefPtr<CefBrowser> browser) const;

private:
    CefWebEnginePrivate *mwebEngine;
    CefMessageRouterConfig mmessageRouterConfig;
    CefRefPtr<CefViewQueryHandler> mcefqueryHandler;
    CefRefPtr<CefMessageRouterBrowserSide> mmessageRouter;
    CefRefPtr<CefResourceManager> mresourceManager;

    IMPLEMENT_REFCOUNTING(InternalCefClient);
};

class CefWebEnginePrivate
{
    Q_DECLARE_PUBLIC(CefWebEngine);
    CefWebEngine *q_ptr;

public:
    CefWebEnginePrivate(CefWebEngine *q);

    void addSwitch(const QString &key);
    void addSwitchWithValue(const QString &key, const QString &value);
    
    CefRefPtr<InternalCefApp> mapp;
    CefRefPtr<InternalCefClient> mclient;
    QList<CefWebEnginePage *> mpages;
    QMap<QString, QString> mswitchs;
};